from collections import Counter, defaultdict
import random
import subprocess
from src.utils.logger import log_ffmpeg

def generate_video_matrix(
    video_source_structure,
    target_videos=70,
    min_clips_per_video=2,
    local_repeat_len=2,
    local_repeat_max=1
):
    """
    根据 video_source_structure 生成组合矩阵，每个组合从不同文件夹中抽取片段。
    clips_per_video为8~文件夹数之间随机。
    可设置局部重复子序列长度(local_repeat_len)和最大允许出现次数(local_repeat_max)。
    """
    num_folders = len(video_source_structure)
    if num_folders < min_clips_per_video:
        raise ValueError(f"文件夹数量必须大于等于最小片段数 {min_clips_per_video}")
    folders = {folder["folder"]: folder["videos"] for folder in video_source_structure}
    matrix = []
    used_combinations = set()
    subseq_counter = defaultdict(int)
    attempts = 0
    max_attempts = target_videos * 200

    while len(matrix) < target_videos and attempts < max_attempts:
        attempts += 1
        clips_per_video = random.randint(min_clips_per_video, num_folders)
        selected_folders = random.sample(range(num_folders), clips_per_video)
        video_column = [None] * num_folders
        for folder_idx in selected_folders:
            clips = folders[folder_idx]
            clip_counts = Counter([row[folder_idx] for row in matrix if row[folder_idx] is not None])
            min_count = min([clip_counts.get(clip, 0) for clip in clips])
            candidates = [clip for clip in clips if clip_counts.get(clip, 0) == min_count]
            clip_id = random.choice(candidates)
            video_column[folder_idx] = clip_id
        comb_key = tuple(video_column)
        if comb_key in used_combinations:
            continue

        # 检查局部重复子序列
        combo_seq = [(idx, video_column[idx]) for idx in range(num_folders) if video_column[idx] is not None]
        has_local_repeat = False
        for i in range(len(combo_seq) - local_repeat_len + 1):
            subseq = tuple(combo_seq[i:i+local_repeat_len])
            if subseq_counter[subseq] >= local_repeat_max:
                has_local_repeat = True
                break
        if has_local_repeat:
            continue

        # 通过检查，添加到结果
        used_combinations.add(comb_key)
        matrix.append(video_column)
        # 更新子序列计数
        for i in range(len(combo_seq) - local_repeat_len + 1):
            subseq = tuple(combo_seq[i:i+local_repeat_len])
            subseq_counter[subseq] += 1

    # 转置为列为组合
    final_matrix = list(map(list, zip(*matrix)))
    return final_matrix

def run_ffmpeg(cmd, operation_name, startupinfo=None, logger=None):
    """
    执行ffmpeg命令并记录日志
    
    :param cmd: ffmpeg命令列表
    :param operation_name: 操作描述
    :param startupinfo: startupinfo对象
    :param logger: 日志记录器
    :return: subprocess.CompletedProcess对象
    :raises: RuntimeError: 当命令执行失败时
    """
    def _log_error(message):
        if logger:
            logger(message, level="error")
        else:
            from src.utils.logger import log_error
            log_error(message)
            
    try:
        # 捕获输出
        process = subprocess.run(
            cmd, 
            startupinfo=startupinfo,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True,
            encoding='latin-1',      # 使用latin-1编码，可以处理任何字节
            check=False  # 自己处理错误
        )
        
        if process.returncode == 0:
            log_ffmpeg(cmd, operation_name, success=True, debug=True)
            return process
        else:
            # 记录失败详情
            log_ffmpeg(cmd, operation_name, success=False)
            _log_error(f"[FFMPEG错误] {operation_name}失败，返回码：{process.returncode}")
            
            # 记录错误输出（对排查至关重要）
            error_message = ""
            if process.stderr:
                error_lines = process.stderr.split('\n')
                filtered_errors = [line for line in error_lines if "error" in line.lower()]
                
                if filtered_errors:
                    error_detail = '|'.join(filtered_errors)
                    _log_error(f"[FFMPEG错误详情] {error_detail}")
                    error_message = error_detail
                else:
                    # 如果没有明确错误行，记录全部输出（但限制长度）
                    error_output = process.stderr
                    if len(error_output) > 1000:
                        error_output = error_output[:500] + "...\n[输出过长已截断]...\n" + error_output[-500:]
                    _log_error(f"[FFMPEG完整错误输出] {error_output}")
                    error_message = error_output[:200] + "..." if len(error_output) > 200 else error_output
            
            # 使用RuntimeError而不是CalledProcessError，提供更清晰的错误信息
            raise RuntimeError(f"FFMPEG操作 '{operation_name}' 失败: {error_message}")
            
    except Exception as e:
        _log_error(f"[FFMPEG执行异常] {operation_name}: {str(e)}")
        # 确保错误被传播
        if not isinstance(e, RuntimeError):
            raise RuntimeError(f"FFMPEG操作 '{operation_name}' 发生异常: {str(e)}")
        raise